package cn.com.chengmandian.bike.log;

import cn.com.chengmandian.agent.utils.AuthUtil;
import cn.com.chengmandian.agent.utils.ServletUtil;
import cn.com.chengmandian.core.common.constants.ResponseCode;
import cn.com.chengmandian.core.web.pojo.AgentUser;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.core.web.utils.SpringContextUtil;
import cn.com.chengmandian.service.bike.constants.UserTypeEnum;
import cn.com.chengmandian.sls.SlsBatchProducer;
import cn.com.chengmandian.sls.pojo.CommonLogData;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;

@Slf4j
@Aspect
@Component
public class ActionLogAspect {
    @Resource
    private SlsBatchProducer slsBatchProducer;

    @Resource
    private LogService logService;

    private final static String controllerName = "Controller";

    private static String[] skitUrl = new String[] {"/bikeuser/rent/combo/buy/enable",
        "/bikeuser/ride/order/path/list",
        "/bikeuser/common/geocoder",
        "/bikeuser/common/upload"};

    /**
     * 请求操作日志
     */
    @AfterReturning(pointcut = "@annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping)", returning = "result")
    public void afterReturningLog(JoinPoint jp, Object result) {
        try {
            if(!jp.getTarget().getClass().getName().contains(controllerName)) return; // 排除微服务调用
            AgentUser agentUser = AuthUtil.getAgentUser();
            if (agentUser == null) return;
            HttpServletRequest request = ServletUtil.getRequest();
            if (ArrayUtil.contains(skitUrl, request.getRequestURI())) {
                return;
            }
            if (request.getRequestURI().contains("/inner/")) {
                return;
            }
            Integer code = null;
            String message = null;
            if (result.getClass() == AjaxResult.class) {
                AjaxResult res = (AjaxResult)result;
                code = res.getCode();
                message = res.getMessage();
            } else if (result.getClass() == HashMap.class) {
                code = ResponseCode.SUCCESS;
            }
            MethodSignature signature = (MethodSignature)jp.getSignature();
            Method method = signature.getMethod();
            ActionLog actionLog = method.getAnnotation(ActionLog.class);
            String actionName = null;
            String actionDescription = null;
            if (actionLog != null) {
                actionName = actionLog.name();
                actionDescription = actionLog.description().contains("#") ? getValBySpEL(actionLog.description(), method, jp.getArgs()) : actionLog.description();
            }
            this.add(agentUser,
                code,
                message,
                request,
                jp.getArgs(),actionName, actionDescription);

            // 新版
            logService.pushLog(
                    LocalMapUtil.getAgentInfo().getId(),
                    request,
                    ServletUtil.getIp(),
                    jp,
                    result,
                    UserTypeEnum.getCodeByUserCode(agentUser.getType()),
                    agentUser.getId(),
                    agentUser.getName()
            );
        } catch (Exception e) {
            log.error("afterReturningLog error!", e);
        }
    }



    private void add(AgentUser agentUser, Integer resultCode, String resultMessage, HttpServletRequest request, Object[] args, String actionName, String actionDescription) {
        try {
            CommonLogData logData = new CommonLogData();
            logData.setAgentId(LocalMapUtil.getAgentInfo().getId());
            logData.setAppCode(SpringContextUtil.getAppName());
            logData.setEnv(SpringContextUtil.getEnv());
            logData.setDomain(request.getServerName());
            logData.setUserType(UserTypeEnum.getCodeByUserCode(agentUser.getType()));
            logData.setUserId(agentUser.getId());
            logData.setUserName(agentUser.getName());
            if (StrUtil.isNotEmpty(actionName)) {
                logData.setActionName(actionName);
                logData.setActionDescription(actionDescription);
            }
            logData.setMethod(request.getMethod());
            logData.setUrl(request.getRequestURI());
            logData.setParams(JSONObject.toJSONString(request.getParameterMap()));
            logData.setBody(JSONObject.toJSONString(args));
            logData.setResultCode(resultCode);
            logData.setResultMessage(resultMessage);
            logData.setIp(ServletUtil.getIp());
            logData.setCreateTime(DateUtil.now());
            slsBatchProducer.sendLog(logData);
        } catch (Exception e) {
            log.error("CommonLog add error:{}", request.getRequestURI());
            log.error("CommonLog add error", e);
        }
    }

    /**
     * 用于SpEL表达式解析.
     */
    private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 解析spEL表达式
     */
    private String getValBySpEL(String spEL, Method method, Object[] args) {
        if(spEL == null) return "";
        String[] spEls = null;
        if(spEL.indexOf("&") >= 0){
            spEls = spEL.split("&");
        }else{
            spEls = new String[]{spEL};
        }
        //获取方法形参名数组
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        if (paramNames != null && paramNames.length > 0) {
            StringBuffer paramContext = new StringBuffer();
            for(String text :spEls) {
                Expression expression = spelExpressionParser.parseExpression(text);
                // spring的表达式上下文对象
                EvaluationContext context = new StandardEvaluationContext();
                // 给上下文赋值
                for (int i = 0; i < args.length; i++) {
                    context.setVariable(paramNames[i], args[i]);
                }
                paramContext.append(expression.getValue(context).toString());
            }
            return paramContext.toString();
        }
        return null;
    }
}
